#ifndef _MAP_H_
#define _MAP_H_

#ifdef _WIN32
#pragma once
#endif

#include <vector>
#include <string>
#include <map>
#include <sstream>
#include <iomanip>
#include <algorithm>

#include "fmod.hpp"

#include "entity.h"
#include "tile.h"
#include "utils.h"
#include "free_tile.h"
#include "repeat.h"
#include "droppable_tile.h"
#include "camera.h"
#include "lightcontainer.h"
#include "light.h"
#include "maploader.h"

#include "skybox.h"

#include "soundmanager.h"

#include "ui.h"
#include "ui_sprite.h"
#include "ui_text.h"

using std::vector;
using std::string;
using std::map;
using std::pair;
using std::stringstream;

using utils::PARAM;
using utils::TYPE_VALUE;

typedef CTile *TILE;
typedef vector<CTile*> ROW;
typedef vector<ROW> FLOOR;
typedef vector<ROW> WIDTHCOLUMN;
typedef vector<ROW> DEPTHCOLUMN;
typedef vector<FLOOR> MAP;

class CPickup;
class CEntity;
class CTile;
class CPlayer;

// Enum to define which tile will be the next tile focused when we attempt
// to index in a spiral pattern for the droppable tiles.
enum TILE_MOVEMENT
{
    NO_MOVEMENT=0,
    MOVE_RIGHT,
    MOVE_DOWN,
    MOVE_LEFT,
    MOVE_UP
};

enum END_STATE
{
    IN_PROGRESS = 0,
    DRAW_WITH_WINNERS,
    DRAW_NO_WINNERS,
    SINGLE_WINNER
};

namespace mapfuncs
{
    void dropTiles(DELAY_PARAMS &params);
    void decrementTime(DELAY_PARAMS &params);
    void moveCamera(DELAY_PARAMS &params);
    //void startMap(DELAY_PARAMS &params);
};

class CMap
{
public:

    CMap(bool bDemo=false);
    ~CMap();

    int getHeight() const;
    int getWidth() const;
    int getDepth() const;

    BYTE getMaxPlayerCount() const;
    int getPickupCount() const;
    int getTileCount() const;
    int getDestructibleTileCount() const;
    int getEntityCount() const;

    short int getTileID(int nHeight, int nWidth, int nDepth);

    CTile* getTile(int x, int y, int z) const;
    FLOOR getFloor(int y) const;
    WIDTHCOLUMN getWidthColumn(int z) const;
    DEPTHCOLUMN getDepthColumn(int x) const;
    MAP getCube(int y, int x, int z, int radius) const;
    MAP getCube(int y, int x, int z, int height, int width, int depth) const;

    void getTilePositionFromCoord(int &nHeight, int &nWidth, int &nDepth,
                                  float fX, float fY, float fZ, int nOffset) 
                                  const;

    void dropTiles();

    void setMapName(string szMapName);
    void setSize(short int nHeight, short int nWidth, short int nDepth);
    void setPickupCount(short int nPickupCount);
    void setTileCount(long int nTileCount);
    void setEntityCount(short int nEntityCount);
    void setMaxPlayerCount(BYTE nPlayerCount);

    void addPickup(CPickup *pPickup);
    void addEntity(CEntity *pEntity);
    void addFloor(FLOOR &floor);
    void addFloorIds(vector<vector<short int>> arrTileIds);

    void setTile(int y, int x, int z, CTile *tile);
    CEntity* getEntityFromID(int index);

    void decrementTime();

    void startGameForReal();

    bool isValidTile(unsigned int nHeight, unsigned int nWidth, 
        unsigned int nDepth) const;

    void render() const;
    virtual void tick();

    void unload();

    bool setNextTileDroppableIndex();

    void reset();

    void pause();
    void resume();
    bool isPaused() const;

    void deleteEntityFromTile(CEntity *pEntity);
    

    CPickup *getPickupFromIndex(size_t nIndex) const;
    void setPickupIdCount(short int nPickupID, long int nPickupCount);
    vector <short int> getPickupIds() const;
    long int getPickupIdCount(short int nPickupID) const;

    void addDestructibleTileCount(short int nTileID, short int nAmount);
    vector<short int> getDestructibleTileIds() const;
    short int getDestructibleIdCount(short int nTileID);

    void setSkyBox(string szSkyboxPath);

    void showWinner(CEntity *pEntity);
    void drawMatch(bool bAnyAlivePlayers);

    void resetCameraToDefault();

    bool isDemo() const;

private:
    void endGame();

    END_STATE m_nEndState;
    TILE_MOVEMENT m_nTileMovement;

    int m_nTimeLeft;
    int m_nTimeCountingDown;

    bool m_bPaused;

    bool m_bDemo;

    MAP m_MapData;
    vector<CPickup*> m_arrPickups;
    vector<CEntity*> m_arrEntitys;

    vector<short int> m_arrEntityIDs;
    vector<vector<vector<short int>>> m_arrTileIDs;
    map<short int, long> m_arrPickupIds;

    map<short int, short int> m_arrDestructibleTiles;

    // Map name
    string m_szMapName;
    
    // Map Size
    short int m_nHeight;
    short int m_nWidth;
    short int m_nDepth;

    float m_fDroppableXPosition;
    float m_fDroppableZPosition;

    float m_fDroppableXMax;
    float m_fDroppableZMax;
    float m_fDroppableXMin;
    float m_fDroppableZMin;

    // Amount of things on the map 
    short int m_nAmountOfPickups;
    long int m_nAmountOfTiles;
    short int m_nAmountOfEntities;
    BYTE m_nNoOfPlayers;

    CUISprite *m_pBackground;
    CUIText *m_pMapTimerUI;
    CUISprite *m_pPauseBackground;
    CUISprite *m_pEndBackground;

    //CRepeat<void> m_StartRepeatTimer;
    CRepeat<void> m_DropTileRepeat;
    CRepeat<void> m_CounterRepeat;
    CRepeat<void> m_moveCameraRepeat;
    
    CEntity *m_pWinner;

    float m_fCameraRotationAngle;
    float m_fDistanceInterpolation;
    float m_fHeightInterpolation;
    D3DXVECTOR3 m_vecLookatInterpolation;

    FMOD::Channel *m_pGameSound;
    FMOD::Channel *m_pEndSound;

    float m_fIntroCameraMovementInterpolation;

};

#endif